﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Windows.Automation.Peers;
using Microsoft.Maps.MapControl.AutomationPeers;
using Microsoft.Maps.MapControl.Core;
using Microsoft.Maps.MapControl;

namespace MyFloridaHouse.libSvBingMapsMultiShape
{
    /// <summary>
    /// This class is a modified version of the Microsoft.Maps.MapControl.Core.MapShapeBase class. 
    /// This clas shas been modified to handle shapes that have multiple LocationCollections worth of data.
    ///   Downloaded from http://cid-e7dba9a4bfd458c5.skydrive.live.com/self.aspx/VE%20Sample%20code/BingSLMultiShapes.zip
    ///   Explanation found at : http://rbrundritt.wordpress.com/2010/03/06/multipolygon-multilinestring-classes-for-bing-maps-silverlight/
    /// </summary>
    public abstract class MultiMapShapeBase : UserControl, IProjectable
    {
         // Fields
        private static readonly DependencyProperty LocationsProperty = DependencyProperty.Register("Locations", typeof(IList<LocationCollection>), typeof(MultiMapShapeBase), new PropertyMetadata(new PropertyChangedCallback(MultiMapShapeBase.Locations_Changed)));
        private ProjectionUpdateLevel pendingUpdate = ProjectionUpdateLevel.Full;
        private Shape shape;
        private Point topLeftViewportPoint = new Point();

        // Methods
        protected MultiMapShapeBase(Shape shape)
        {
            base.Content = this.shape = shape;
            base.UseLayoutRounding = false;
            base.Content.UseLayoutRounding = false;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            base.Content.Arrange(new Rect(this.topLeftViewportPoint.X, this.topLeftViewportPoint.Y, base.Content.DesiredSize.Width + 1.0, base.Content.DesiredSize.Height + 1.0));
            return this.ParentMap.ViewportSize;
        }

        private static void Locations_Changed(DependencyObject o, DependencyPropertyChangedEventArgs ea)
        {
            MultiMapShapeBase base2 = o as MultiMapShapeBase;
            if (base2 != null)
            {
                List<LocationCollection> oldValue = ea.OldValue as List<LocationCollection>;
                if (oldValue != null)
                {
                    foreach (LocationCollection lc in oldValue)
                    {
                        lc.CollectionChanged -= new NotifyCollectionChangedEventHandler(base2.Locations_CollectionChanged);
                    }
                }
                List<LocationCollection> newValue = ea.NewValue as List<LocationCollection>;
                if (newValue != null)
                {
                    foreach (LocationCollection lc in newValue)
                    {
                        lc.CollectionChanged += new NotifyCollectionChangedEventHandler(base2.Locations_CollectionChanged);
                    }
                }
                base2.ProjectionUpdated(ProjectionUpdateLevel.Full);
            }
        }

        private void Locations_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            this.ProjectionUpdated(ProjectionUpdateLevel.Full);
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            MapBase parentMap = this.ParentMap;
            if (this.pendingUpdate != ProjectionUpdateLevel.None)
            {
                if (this.Vertices != null)
                {
                    Point point3;
                    if ((this.pendingUpdate == ProjectionUpdateLevel.Full) && (this.Vertices.Count > 0) && (this.Vertices[0].Count > 0))
                    {
                        List<PointCollection> pointList = new List<PointCollection>();
                        List<PointCollection> projectedPoints = new List<PointCollection>();

                        //The smallest point to be used as the offset for all other points
                        Point point = new Point(double.MaxValue, double.MaxValue);

                        foreach (LocationCollection lc in this.Vertices)
                        {
                            PointCollection points = new PointCollection();
                            
                            //find the smallest point in the coolect
                            foreach (Point point2 in parentMap.Mode.LocationToViewportPoint(lc))
                            {
                                point.X = Math.Min(point.X, point2.X);
                                point.Y = Math.Min(point.Y, point2.Y);
                                points.Add(point2);
                            }

                            pointList.Add(points);
                        }

                        for (int j = 0; j < pointList.Count; j++)
                        {
                            for (int i = 0; i < pointList[j].Count; i++)
                            {
                                Point point4 = pointList[j][i];
                                pointList[j][i] = new Point(point4.X - point.X, point4.Y - point.Y);
                            }

                            projectedPoints.Add(pointList[j]);
                        }
                        this.ProjectedPoints = projectedPoints;
                    }
                    if (((this.ProjectedPoints.Count > 0) && (this.ProjectedPoints[0].Count > 0) && (this.Vertices.Count > 0) && (this.Vertices[0].Count > 0)) && parentMap.TryLocationToViewportPoint(this.Vertices[0][0], out point3))
                    {
                        Point point5 = this.ProjectedPoints[0][0];
                        this.topLeftViewportPoint = new Point(point3.X - point5.X, point3.Y - point5.Y);
                    }
                }
                else
                {
                    this.ProjectedPoints.Clear();
                }
                this.pendingUpdate = ProjectionUpdateLevel.None;
            }
            base.Content.Measure(new Size(double.MaxValue, double.MaxValue));
            return parentMap.ViewportSize;
        }

        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new BaseAutomationPeer(this, "MapShape");
        }

        public void ProjectionUpdated(ProjectionUpdateLevel updateLevel)
        {
            if (updateLevel != ProjectionUpdateLevel.None)
            {
                base.InvalidateMeasure();
                base.InvalidateArrange();
                this.pendingUpdate |= updateLevel;
            }
        }

        // Properties
        protected Shape EncapsulatedShape
        {
            get
            {
                return this.shape;
            }
        }

        public Brush Fill
        {
            get
            {
                return (Brush) this.shape.GetValue(Shape.FillProperty);
            }
            set
            {
                this.shape.SetValue(Shape.FillProperty, value);
            }
        }

        public IList<LocationCollection> Vertices
        {
            get
            {
                return (IList<LocationCollection>)base.GetValue(LocationsProperty);
            }
            set
            {
                base.SetValue(LocationsProperty, value);
            }
        }

        public double Opacity
        {
            get
            {
                return base.Opacity;
            }
            set
            {
                base.Opacity = value;
            }
        }

        public Brush OpacityMask
        {
            get
            {
                return base.OpacityMask;
            }
            set
            {
                base.OpacityMask = value;
            }
        }

        public MapBase ParentMap
        {
            get
            {
                IProjectable parent = base.Parent as IProjectable;
                if (parent == null)
                {
                    throw new InvalidOperationException("Invalid MapBase");
                }
                return parent.ParentMap;
            }
        }

        public abstract IList<PointCollection> ProjectedPoints { get; set; }
        
        public Brush Stroke
        {
            get
            {
                return (Brush) this.shape.GetValue(Shape.StrokeProperty);
            }
            set
            {
                this.shape.SetValue(Shape.StrokeProperty, value);
            }
        }

        public DoubleCollection StrokeDashArray
        {
            get
            {
                return (DoubleCollection) this.shape.GetValue(Shape.StrokeDashArrayProperty);
            }
            set
            {
                this.shape.SetValue(Shape.StrokeDashArrayProperty, value);
            }
        }

        
        public PenLineCap StrokeDashCap
        {
            get
            {
                return (PenLineCap) this.shape.GetValue(Shape.StrokeDashCapProperty);
            }
            set
            {
                this.shape.SetValue(Shape.StrokeDashCapProperty, value);
            }
        }

        
        public double StrokeDashOffset
        {
            get
            {
                return (double) this.shape.GetValue(Shape.StrokeDashOffsetProperty);
            }
            set
            {
                this.shape.SetValue(Shape.StrokeDashOffsetProperty, value);
            }
        }

        
        public PenLineCap StrokeEndLineCap
        {
            get
            {
                return (PenLineCap) this.shape.GetValue(Shape.StrokeEndLineCapProperty);
            }
            set
            {
                this.shape.SetValue(Shape.StrokeEndLineCapProperty, value);
            }
        }

        
        public PenLineJoin StrokeLineJoin
        {
            get
            {
                return (PenLineJoin) this.shape.GetValue(Shape.StrokeLineJoinProperty);
            }
            set
            {
                this.shape.SetValue(Shape.StrokeLineJoinProperty, value);
            }
        }

        
        public double StrokeMiterLimit
        {
            get
            {
                return (double) this.shape.GetValue(Shape.StrokeMiterLimitProperty);
            }
            set
            {
                this.shape.SetValue(Shape.StrokeMiterLimitProperty, value);
            }
        }

        
        public PenLineCap StrokeStartLineCap
        {
            get
            {
                return (PenLineCap) this.shape.GetValue(Shape.StrokeStartLineCapProperty);
            }
            set
            {
                this.shape.SetValue(Shape.StrokeStartLineCapProperty, value);
            }
        }

        
        public double StrokeThickness
        {
            get
            {
                return (double) this.shape.GetValue(Shape.StrokeThicknessProperty);
            }
            set
            {
                this.shape.SetValue(Shape.StrokeThicknessProperty, value);
            }
        }
    }
}